﻿using System;
using System.Threading;
using System.Threading.Tasks;

namespace CSharpConsoleTestSolution.Demo {
    
    /**
     * 什么是委托
     * 委托 (delegate) 是函数指针的“升级版"
     *   ·实例 : C/C++中的函数指针:
     *          typedef int(*Calc) (int a, int b);  //这就是C中的 函数指针
     *          int main() {
     *              Calc funcAdd = &Add;    //Add 是一个 Add(int a, intb) 方法
     *              Calc funcSub = &Sub;
     *              int x = funcAdd(2, 3);  //5
     *              int y = funcSub(2, 3);  //-1
     *          }
     * 
     * 一切皆地址
     *   ·变量(数据)是以某个地址为起点的一段内存中所存储的值
     *   ·函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
     * 
     * 直接调用与间接调用
     *   ·直接调用: 通过函数名来调用函数，CPU通过函数名直接获得函数所在地址并开始执行 -> 返回
     *   ·间接调用: 通过函数指针来调用函数，CPU通过读取函数指针存储的值获得函数所在地址并开始执行 -> 返回
     *
     * Java中没有与委托相对应的功能实体
     *
     * 委托的简单使用
     *   ·Action委托
     *   ·Func委托
     */
    public class P19_delegate {

        public static void Action_delegate() {
            Console.WriteLine("1.Action 委托: void (参数0~15个) { ... }");
            Console.WriteLine("var calculator = new Calculator();");
            Console.WriteLine("Action actionPrint = new Action(calculator.Print);");
            Console.WriteLine("actionPrint = calculator.Print;  //一样的");
            Console.WriteLine("actionPrint.Invoke();    //间接调用");
            Console.WriteLine("actionPrint();");
            
            var calculator = new Calculator();
            Action actionPrint = new Action(calculator.Print);
            // actionPrint = calculator.Print;  //一样的
            actionPrint.Invoke();    //间接调用
            actionPrint();
        }

        public static void Func_delegate() {
            Console.WriteLine();
            Console.WriteLine("2.Func 委托: TResult (参数0~15个) { ... }");
            Console.WriteLine("Func<int, int, float> funcAdd = new Func<int, int, float>(calculator.Add);");
            Console.WriteLine("Func<int, int, float> funcSub = calculator.Sub;  //也阔以");
            Console.WriteLine("var resultAdd = funcAdd.Invoke(2, 3);");
            Console.WriteLine("var resultSub = funcSub.Invoke(2, 3);");
            
            var calculator = new Calculator();
            Func<int, int, float> funcAdd = new Func<int, int, float>(calculator.Add);
            Func<int, int, float> funcSub = calculator.Sub;
            var resultAdd = funcAdd.Invoke(2, 3);
            var resultSub = funcSub.Invoke(2, 3);
            Console.WriteLine($"resultAdd={resultAdd}, resultSub={resultSub}");
        }

        public static void Predicate_delegate() {
            Console.WriteLine();
            Console.WriteLine("3.Predicate 委托: bool (参数x1) { ... }");
            Console.WriteLine("Predicate<int> predicate = calculator.IsBigerThan10;");
            Console.WriteLine("var result = predicate.Invoke(11);");
            
            var calculator = new Calculator();
            Predicate<int> predicate = calculator.IsBiggerThan10;
            var result = predicate.Invoke(11);
            Console.WriteLine($"result = {result}");
        }

        /**
         * 4.自定义委托: delegate double Calc(double x, double y), 时间: 17:22
         * 委托的声明(自定义委托)
         *   ·委托是一种类( class )，类是数据类型, 所以委托也是一种数据类型
         *   ·它的声名方式与一般的类不同，主要是为了照顾可读性和C/C++传统
         *   ·注意声明委托的位置
         *       ·避免写错地方结果声明成嵌套类型
         *   ·委托与所封装的方法必需”类型兼容"
         */
        public static void Custom_delegate() {
            Console.WriteLine();
            Console.WriteLine("4.自定义委托: delegate [void|TResult] (参数0~15个) { ... }");
            Console.WriteLine("//自定义委托类型 (是1个类, 一般写在 namespace 里面, 当然也可以写在class 里面)");
            Console.WriteLine("public delegate float Calc(int a, int b);");
            Console.WriteLine("Calc calcAdd = calculator.Add;");
            Console.WriteLine("Calc calcSub = calculator.Sub;");
            Console.WriteLine("var resultAdd = calcAdd.Invoke(2, 3);");
            Console.WriteLine("var resultSub = calcSub.Invoke(2, 3);");
        
            Calculator calculator = new Calculator();
            Calc calcAdd = calculator.Add;
            Calc calcSub = calculator.Sub;
            var resultAdd = calcAdd.Invoke(2, 3);
            var resultSub = calcSub.Invoke(2, 3);
            Console.WriteLine($"resultAdd={resultAdd}, resultSub={resultSub}");
        }



        private class Calculator {

            public void Print() {
                Console.WriteLine("I have 5 methods!");
            }

            public bool IsBiggerThan10(int a) {
                return a > 10;
            }

            public float Add(int a, int b) {
                return a + b;
            }
            
            public float Sub(int a, int b) {
                return a - b;
            }
        }


        /**
         * 5.委托的一般使用, 时间: 26:58
         * 实例:把方法当作参数传给另一个方法
         *   ·正确使用1: 模板方法，“借用”指定的外部方法来产生结果
         *      ·相当于“填空题
         *      ·常位于代码中部
         *      ·委托有返回值
         *   ·正确使用2: 回调(callback)方法，调用指定的外部方法
         *      ·相当于“流水线”
         *      ·常位于代码末尾
         *      ·委托无返回值
         * 
         * 注意: 难精通+易使用+功能强大东西，一旦被滥用则后果非常严重
         *   ·缺点1: 这是一种方法级别的紧耦合，现实工作中要慎之又慎
         *   ·缺点2: 使可读性下降、debug的难度增加
         *   ·缺点3: 把委托回调、异步调用和多线程纠缠在一起，会让代码变得难以阅读和维护
         *   ·缺点4: 委托使用不当有可能造成内存泄漏和程序性能下降
         */
        public static void usage_delegate() {
            Console.WriteLine();
            Console.WriteLine("5.委托的一般使用");
            Console.WriteLine("正确使用1: 模板方法，“借用”指定的外部方法来产生结果");
            Console.WriteLine("var wFactory = new WrapFactory();");
            Console.WriteLine("var pFactory = new ProductFactory();");
            Console.WriteLine("Func<Product> func0 = pFactory.MakePizza;");
            Console.WriteLine("Func<Product> func1 = pFactory.MakeToyCar;");
            Console.WriteLine("//WrapFactory的模板方法: public Box WrapProduct(Func<Product> getProductFunc)");
            Console.WriteLine("var box0 = wFactory.WrapProduct(func0);");
            Console.WriteLine("var box1 = wFactory.WrapProduct(func1);");

            var wFactory = new WrapFactory();
            var pFactory = new ProductFactory();
            Func<Product> func0 = pFactory.MakePizza;
            Func<Product> func1 = pFactory.MakeToyCar;
            var box0 = wFactory.WrapProduct(func0);
            var box1 = wFactory.WrapProduct(func1);
            Console.WriteLine($"box0.Product.Name = {box0.Product.Name}");
            Console.WriteLine($"box1.Product.Name = {box1.Product.Name}");
            
            
            Console.WriteLine();
            Console.WriteLine("正确使用2: 回调(callback)方法，调用指定的外部方法");
            Console.WriteLine("略过...");
        }

        private class Product { public string Name { get; set; } }
        private class Box { public Product Product { get; set; } }

        private class WrapFactory {
            public Box WrapProduct(Func<Product> getProductFunc) {
                return new Box { Product = getProductFunc.Invoke() };
            }
        }
        private class ProductFactory {
            public Product MakePizza() {
                return new Product { Name = "Pizza"};
            }

            public Product MakeToyCar() {
                return new Product { Name = "ToyCar" };
            }
        }



        /**
         * 6.委托的高级使用, 时间: 54:40
         * ·多播( multicast) 委托
         */
        public static void multicast_delegate() {
            Console.WriteLine();
            Console.WriteLine("6.委托的高级使用");
            Student student0 = new Student { Id = 0, PenColor = ConsoleColor.Red };
            Student student1 = new Student { Id = 1, PenColor = ConsoleColor.Green };
            Student student2 = new Student { Id = 2, PenColor = ConsoleColor.Blue };

            Console.WriteLine("//单播委托: 1个委托封装1个方法的形式");
            Console.WriteLine("Action action0 = student0.DoHomework;");
            Console.WriteLine("Action action1 = student1.DoHomework;");
            Console.WriteLine("Action action2 = student2.DoHomework;");
            Console.WriteLine("action0.Invoke();");
            Console.WriteLine("action1.Invoke();");
            Console.WriteLine("action2.Invoke();");
            Action action0 = student0.DoHomework;
            Action action1 = student1.DoHomework;
            Action action2 = student2.DoHomework;
            action0.Invoke();
            action1.Invoke();
            action2.Invoke();
            
            Console.WriteLine();
            Console.WriteLine("//多播( multicast) 委托");
            Console.WriteLine("action0 += action1; //将1合并到0");
            Console.WriteLine("action0 += action2; //将2合并到0");
            Console.WriteLine("action0.Invoke();");
            action0 += action1; //将1合并到0
            action0 += action2; //将2合并到0
            action0.Invoke();
        }

        private class Student {
            public int Id { get; set; }
            public ConsoleColor PenColor { get; set; }

            public void DoHomework() {
                for (int i = 0; i < 5; i++) {
                    Console.ForegroundColor = PenColor;
                    Console.WriteLine($"Student {Id} doing homework {i} hour(s).");
                    Thread.Sleep(1000);
                }
            }
        }



        /**
         * 7.委托的高级使用, 时间: 1:00:13
         * 
         * ·隐式异步调用
         *    ·同步与异步的简介
         *       ·中英文的语言差异
         *       ·同步: 你做完了我(在你的基础上)接着做
         *       ·异步: 咱们两个同时做(相当于汉语中的”同步进行”)
         * 
         *    ·同步调用与异步调用的对比
         *       ·每一个运行的程序是一个进程( process )
         *       ·每个进程可以有一个或者多个线程( thread )
         *       ·同步调用是在同一线程内
         *       ·异步调用的底层机理是多线程
         *       ·串行==同步==单线程，并行==异步==多线程
         * 
         *    ·隐式多线程 V.S. 显式多线程
         *       ·直接同步调用: 使用方法名
         *       ·间接同步调用: 使用单播/多播委托的 Invoke 方法
         *       ·隐式异步调用: 使用委托的 BeginInvoke
         *       ·显式异步调用: 使用 Thread 或 Task
         * 
         * ·应该适时地使用接口 (interface)取代一些对委托的使用
         *    ·Java完全地使用接口取代了委托的功能，即Java没有与C#中委托相对应的功能实体
         */
        public static void hide_async_delegate() {
            Console.WriteLine();
            Console.WriteLine("7.委托的高级使用");
            Console.WriteLine("隐式异步调用");
            Console.WriteLine("Action action0 = student0.DoHomework;");
            Console.WriteLine("Action action1 = student1.DoHomework;");
            Console.WriteLine("Action action2 = student2.DoHomework;");
            Console.WriteLine("//隐式异步调用: 使用委托的 BeginInvoke");
            Console.WriteLine("IAsyncResult asyncResult0 = action0.BeginInvoke((AsyncCallback) null, (object) null);");
            Console.WriteLine("IAsyncResult asyncResult1 = action1.BeginInvoke((AsyncCallback) null, (object) null);");
            Console.WriteLine("IAsyncResult asyncResult2 = action2.BeginInvoke((AsyncCallback) null, (object) null);");
            Console.WriteLine();
            Console.WriteLine("for (int i = 0; i < 5; i++) {");
            Console.WriteLine("    Console.ForegroundColor = ConsoleColor.Cyan;");
            Console.WriteLine("    Console.WriteLine($\"Main Thread {i}.\");");
            Console.WriteLine("    Thread.Sleep(1000);");
            Console.WriteLine("}");
            
            var student0 = new Student { Id = 0, PenColor = ConsoleColor.Red };
            var student1 = new Student { Id = 1, PenColor = ConsoleColor.Green };
            var student2 = new Student { Id = 2, PenColor = ConsoleColor.Blue };
            Action action0 = student0.DoHomework;
            Action action1 = student1.DoHomework;
            Action action2 = student2.DoHomework;
            //隐式异步调用: 使用委托的 BeginInvoke
            IAsyncResult asyncResult0 = action0.BeginInvoke((AsyncCallback) null, (object) null);
            IAsyncResult asyncResult1 = action1.BeginInvoke((AsyncCallback) null, (object) null);
            IAsyncResult asyncResult2 = action2.BeginInvoke((AsyncCallback) null, (object) null);

            for (int i = 0; i < 5; i++) {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine($"Main Thread {i}.");
                Thread.Sleep(1000);
            }
        }

        /**
         * 显式异步调用, 时间: 1:11:05
         */
        public static void show_async_delegate() {
            Console.WriteLine();
            Console.WriteLine("显式异步调用: 使用 Thread 或 Task");
            Console.WriteLine("使用 Thread 的方式:");
            Console.WriteLine("var trThread0 = new Thread(new ThreadStart(action0));");
            Console.WriteLine("var trThread1 = new Thread(new ThreadStart(action1));");
            Console.WriteLine("var trThread2 = new Thread(new ThreadStart(action2));");
            Console.WriteLine("trThread0.Start();");
            Console.WriteLine("trThread1.Start();");
            Console.WriteLine("trThread2.Start();");
            Console.WriteLine();
            Console.WriteLine("for (int i = 0; i < 5; i++) {");
            Console.WriteLine("    Console.ForegroundColor = ConsoleColor.Cyan;");
            Console.WriteLine("    Console.WriteLine($\"Main Thread {i}.\");");
            Console.WriteLine("    Thread.Sleep(1000);");
            Console.WriteLine("}");
            
            var student0 = new Student { Id = 0, PenColor = ConsoleColor.Red };
            var student1 = new Student { Id = 1, PenColor = ConsoleColor.Green };
            var student2 = new Student { Id = 2, PenColor = ConsoleColor.Blue };
            Action action0 = student0.DoHomework;
            Action action1 = student1.DoHomework;
            Action action2 = student2.DoHomework;
            var trThread0 = new Thread(new ThreadStart(action0));
            var trThread1 = new Thread(new ThreadStart(action1));
            var trThread2 = new Thread(new ThreadStart(action2));
            trThread0.Start();
            trThread1.Start();
            trThread2.Start();
            
            for (int i = 0; i < 5; i++) {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine($"Main Thread {i}.");
                Thread.Sleep(1000);
            }
            
            
            Console.WriteLine();
            Console.WriteLine("使用 Task 的方式:");
            Console.WriteLine("var task0 = new Task(action0);");
            Console.WriteLine("var task1 = new Task(action1);");
            Console.WriteLine("task0.Start();");
            Console.WriteLine("task1.Start();");
            Console.WriteLine("Task.Run(() => { action2.Invoke(); });");
            Console.WriteLine();
            Console.WriteLine("for (int i = 0; i < 5; i++) {");
            Console.WriteLine("    Console.ForegroundColor = ConsoleColor.Cyan;");
            Console.WriteLine("    Console.WriteLine($\"Main Thread {i}.\");");
            Console.WriteLine("    Thread.Sleep(1000);");
            Console.WriteLine("}");
            
            var task0 = new Task(action0);
            var task1 = new Task(action1);
            task0.Start();
            task1.Start();
            // Task.Run(action2);   //一样的
            Task.Run(() => {
                action2.Invoke();
            });
            
            for (int i = 0; i < 5; i++) {
                Console.ForegroundColor = ConsoleColor.Cyan;
                Console.WriteLine($"Main Thread {i}.");
                Thread.Sleep(1000);
            }
        }

        /**
         * 9.静态方法 委托
         */
        public static void static_delegate() {
            Console.WriteLine();
            Console.WriteLine("9.静态方法 委托");
            Console.WriteLine("Action action0 = Console.WriteLine;");
            Console.WriteLine("Action<string> action1 = Console.WriteLine;");
            Console.WriteLine("action.Invoke();");
            Console.WriteLine("action1.Invoke(\"Hello World!\");");
            Action action0 = Console.WriteLine;
            Action<string> action1 = Console.WriteLine;
            action0.Invoke();
            action1.Invoke("Hello World!");
        }
    }

    //自定义委托类型 (是1个类, 写在 namespace 里面)
    public delegate float Calc(int a, int b);
}